﻿using MediaFoundation.Misc;
using MediaFoundation.ReadWrite;
using MediaFoundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.IO;
using System.Windows.Media.Imaging;

namespace CameraMF.NET6
{
    class MFCaptureSync
    {
        private IMFMediaSource mediaSource;
        private IMFAttributes attribute;
        private IMFActivate[] activateDevices;
        public string deviceName; // 设备名字
        HResult Result;
        public MFCaptureSync()
        {
            MFExtern.MFStartup(0x20070, MFStartup.Lite);
        }

        /// <summary>
        /// 打开摄像头设备
        /// </summary>
        public void OpenCaptureDevice()
        {
            Result = MFExtern.MFCreateAttributes(out attribute, 1); // 创建一个属性
            if (Result != 0) return;
            attribute.SetGUID(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, CLSID.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); // 设置属性
            int devicescount;
            MFExtern.MFEnumDeviceSources(attribute, out activateDevices, out devicescount); // 枚举满足属性的摄像头设备
            if (Result != 0) return;
            if (devicescount == 0) return;
            for (int i = 0; i < devicescount; i++)
            {
                activateDevices[i].GetAllocatedString(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, out deviceName, out int length);
                Console.WriteLine(deviceName);
            }
            {
                activateDevices[0].GetAllocatedString(MFAttributesClsid.MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, out deviceName, out int length);
                Console.WriteLine(deviceName);
            }
            
            activateDevices[0].ActivateObject(typeof(IMFMediaSource).GUID, out object mediaSourceObj); // 激活设备
            mediaSource = mediaSourceObj as IMFMediaSource;
        }

        private IMFPresentationDescriptor presentationDescriptor = null;
        private IMFStreamDescriptor streamDescriptor = null;
        private IMFMediaTypeHandler mediatypeHandler = null;
        public int mediatypeCount;
        private IMFMediaType mediaType = null;
        /// <summary>
        /// 枚举摄像头支持的参数，选择匹配的参数捕捉图像
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="fps"></param>
        /// <param name="typename"></param>
        public void SetupCatureType(int width, int height, double fps, string typename)
        {
            if (Result != 0) return;
            // 获得一个表现描述符
            Result = mediaSource.CreatePresentationDescriptor(out presentationDescriptor);
            if (Result != 0) return;
            bool bselected;
            // 从表现描述符中获得流描述符
            Result = presentationDescriptor.GetStreamDescriptorByIndex(0, out bselected, out streamDescriptor);
            if (Result != 0) return;
            // 从流描述器中或得媒体类型操作器
            Result = streamDescriptor.GetMediaTypeHandler(out mediatypeHandler);
            if (Result != 0) return;
            // 获得支持的媒体类型
            Result = mediatypeHandler.GetMediaTypeCount(out mediatypeCount);
            if (Result != 0) return;
            for (int i = 0; i < mediatypeCount; i++)
            {
                Result = mediatypeHandler.GetMediaTypeByIndex(i, out mediaType);
                if (Result != 0) continue;
                long framesize;
                mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_SIZE, out framesize);
                uint w, h;
                w = (uint)(framesize >> 32);
                h = (uint)(framesize & 0xFFFFFFFL);
                long framerate;
                mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_RATE, out framerate);
                int frame = (int)(framerate >> 32);
                int ratio = (int)(framerate & 0xFFFFFFFFL);
                int samplesize;
                mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, out samplesize);
                Guid subtype;
                mediaType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, out subtype);
                Console.WriteLine(w.ToString() + " x " + h.ToString() + " @ " + ((double)frame / ratio).ToString("f1") + "hz"
                    + "\t" + "samplesize:" + samplesize.ToString()
                    + "\t" + "type:" + NameofGUID(subtype));
                if (w == width && h == height && (double)frame / ratio == fps && NameofGUID(subtype) == typename)
                {
                    mediaType.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, subtype);
                    Result = mediatypeHandler.SetCurrentMediaType(mediaType);
                    if (Result != 0) return;
                }
            }
        }

        /// <summary>
        /// 工具函数，根据GUID 找名字
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        private string NameofGUID(Guid guid)
        {
            var names = typeof(MFMediaType).GetFields();
            foreach (var i in names)
            {
                var obj = i.GetValue(null);
                if (obj is Guid && (Guid)obj == guid)
                {
                    return i.Name;
                }
            }
            return "unknown";
        }

        public IntPtr bufptr;
        /// <summary>
        /// 开始捕捉
        /// </summary>
        public void CaptureStart()
        {
            int samplesize;
            if (mediaType == null) throw new Exception("mediatype invailed");
            mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, out samplesize); // 获得一个样本的大小
            bufptr = Marshal.AllocHGlobal(samplesize); // 设置图片的Buffer大小

            var var = new PropVariant();
            mediaSource.Start(presentationDescriptor, System.Guid.Empty, var);
        }

        /// <summary>
        /// 停止捕捉
        /// </summary>
        public void CaptureStop()
        {
            mediaSource.Stop();
            Marshal.FreeHGlobal(bufptr);
        }

        private IMFSourceReader sourceReader; //"70ae66f2-c809-4e4f-8915-bdcb406b7993"
        public int streamIndex;
        private MF_SOURCE_READER_FLAG streamFlags;
        private long timeStamp;
        private IMFSample mfSample;

        #region 同步模式
        private void CreateSourceReaderSync()
        {
            // 创建SourceReader
            var result = MFExtern.MFCreateSourceReaderFromMediaSource(mediaSource, attribute, out sourceReader);
        }

        /// <summary>
        /// 同步的方式 捕捉一帧
        /// </summary>
        public ImageSource Capture()
        {
            HResult result;
            if (sourceReader == null)
            {
                CreateSourceReaderSync();
            }

            result = sourceReader.ReadSample(
                (int)MF_SOURCE_READER.FirstVideoStream,
                MediaFoundation.ReadWrite.MF_SOURCE_READER_CONTROL_FLAG.None, 
                out streamIndex, out streamFlags,  out timeStamp, out mfSample);
            if (result != 0) return null;
            if (mfSample == null) return null;

            int samplesize;
            result = mediaType.GetUINT32(MFAttributesClsid.MF_MT_SAMPLE_SIZE, out samplesize);
            IMFMediaBuffer mediabuf = null;
            result = MFExtern.MFCreateMemoryBuffer(samplesize, out mediabuf);
            result = mfSample.CopyToBuffer(mediabuf); // 拷贝一个样本到MediaBuffer

            int maxlen, curlen; // 图像大小max，jpg的大小cur，压缩jpg大小不定
            mediabuf.GetMaxLength(out maxlen);
            mediabuf.GetCurrentLength(out curlen);

            mediabuf.Lock(out bufptr, out maxlen, out curlen); // 锁定 MediaBuffer
            byte[] managebuf = new byte[maxlen];
            Marshal.Copy(bufptr, managebuf, 0, maxlen); // 从MediaBuffer拷贝到自定的图片buffer

            // 转换buffer成BitmapSource
            using (MemoryStream stream = new MemoryStream(managebuf))
            {
                stream.Seek(0, SeekOrigin.Begin);
                stream.Write(managebuf, 0, managebuf.Length);
                stream.Seek(0, SeekOrigin.Begin);
                ImageSource imagesource = StreamToBitmapSource(stream);
                stream.Close();
                mediabuf.Unlock();
                sourceReader.Flush(streamIndex);
                COMBase.SafeRelease(mfSample);
                return imagesource;
            }
        }

        /// <summary>
        /// 流转成BitmapSource
        /// </summary>
        /// <param name="s">流</param>
        /// <returns></returns>
        public static BitmapSource StreamToBitmapSource(Stream s)
        {
            JpegBitmapDecoder jpg = new JpegBitmapDecoder(s, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); // OnLoad很重要
            return jpg.Frames[0];
        }
    }
}
#endregion